package com.example.my_helper;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class MyTCPServer {

    final static int ON_CONNECT = 0;
    final static int ON_MESSAGE = 1;
    final static int ON_SEND = 2;
    final static int ON_CLOSE = 3;
    final static int ON_ERROR = 4;
    final static int ON_CLIENT_CLOSE = 5;
    final static int ON_CLIENT_CONNECT = 6;

    public String host;
    public int port;
    public ServerSocket serverSocket;

    public Context context;
    public Handler handler;
    NetworkDebuger networkDebuger;

    public boolean isOpen = false;
    private boolean connect_lock = false;
    public int max_size = 5;

    public ArrayList<ServerThread> sockets = new ArrayList<ServerThread>();

    public MyTCPServer(Context context, Handler handler)
    {
        this.context = context;
        this.handler = handler;
    }

    public void WaitConnect(int port)
    {
        this.port = port;
        if(isOpen || connect_lock)
            return;
        connect_lock = true;
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    serverSocket = new ServerSocket(port);
                    isOpen = true;
                    handler.sendEmptyMessage(ON_CONNECT);
                } catch (IOException e) {
                    e.printStackTrace();
                    handler.sendEmptyMessage(ON_ERROR);
                }
                connect_lock = false;
                Socket socket;
                while(isOpen)
                {
                    try {
                        socket = serverSocket.accept();
                        if (sockets.size() < max_size) {
                            ServerThread serverThread = new ServerThread(socket);
                            sockets.add(serverThread);
                            serverThread.start();
                            if(sockets.size() == max_size)
                                Toast.makeText(context, "已达到最大连接数量", Toast.LENGTH_SHORT).show();
                        } else {
                            socket.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    public int Write(int id, byte[] bytes, int offset, int len)
    {
        if (!isOpen || id > sockets.size()) {
            return -1;
        }
        int res = 0;
        if(id == 0){
            for(ServerThread s : sockets){
                res = s.Write(bytes, offset, len);
            }
        }else{
            res = sockets.get(id-1).Write(bytes, offset, len);
        }
        return res;
    }

    public ArrayList<String> GetClientList(){
        if(sockets.size() == 0){
            return null;
        }
        ArrayList<String> clientList = new ArrayList<>();
        for (ServerThread s : sockets){
            clientList.add(s.address);
        }
        return clientList;
    }

    public void Close()
    {
        if(isOpen)
        {
            isOpen = false;
            if (serverSocket != null) {
                try{
                    while(sockets.size() > 0){
                        sockets.get(0).Close();
                    }
                    serverSocket.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
            handler.sendEmptyMessage(ON_CLOSE);
        }
    }

    public void CloseClient(int id)
    {
        if (isOpen && id < sockets.size()){
            try{
                sockets.get(id).Close();
            }catch(Exception e){
                e.printStackTrace();
            }

        }
    }

    class ServerThread extends Thread {
        Socket socket;
        InputStream inputStream;
        OutputStream outputStream;
        String address;
        boolean isConnected = false;
        int buf_len = 0;
        int recv_len = 0;
        int recv_tick = 0;
        byte[] buf;
        int len;
        byte b;

        public ServerThread(Socket socket) throws IOException {
            this.socket = socket;
            this.address = getAddress();
            this.inputStream = socket.getInputStream();
            this.outputStream = socket.getOutputStream();
            this.buf = new byte[512];
            isConnected = true;
            Message msg = new Message();
            msg.what = ON_CLIENT_CONNECT;
            msg.obj = address;
            handler.sendMessage(msg);
        }
        @Override
        public void run() {
            byte[] tmp = new byte[512];
            Message msg;
            while(isOpen && this.isConnected){
                try {
                    b = (byte) inputStream.read();
                    if(b >= 0) {
                        tmp[buf_len++] = b;
                        while (true) {
                            len = inputStream.available();
                            if (len > 0) {
                                recv_tick = 0;
                                for (int i = 0; i < len; i++) {
                                    b = (byte) inputStream.read();
                                    if(b < 0)
                                        break;
                                    tmp[buf_len++] = b;
                                    if(buf_len >= buf.length) {
                                        handler.removeMessages(ON_MESSAGE);
                                        networkDebuger.Log_recv(tmp, buf_len);
                                        /*System.arraycopy(tmp, 0, buf, 0, buf_len);
                                        msg = new Message();
                                        msg.arg1 = buf_len;
                                        msg.obj = buf;
                                        msg.what = ON_MESSAGE;
                                        handler.sendMessage(msg);*/
                                        buf_len = 0;
                                    }
                                }
                            } else {
                                recv_tick++;
                                if(recv_tick > 20){
                                    handler.removeMessages(ON_MESSAGE);
                                    networkDebuger.Log_recv(tmp, buf_len);
                                    /*System.arraycopy(tmp, 0, buf, 0, buf_len);
                                    msg = new Message();
                                    msg.arg1 = buf_len;
                                    msg.obj = buf;
                                    msg.what = ON_MESSAGE;
                                    handler.sendMessage(msg);*/
                                    buf_len = 0;
                                    break;
                                }
                                Thread.sleep(1);
                            }
                        }
                    } else {
                        this.Close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    this.Close();
                }
            }
        }

        public String getAddress(){
            String addr;
            addr = this.socket.getInetAddress().getHostAddress();
            int port = this.socket.getPort();
            return addr+":"+port;
        }

        public int Write(byte[] bytes, int offset, int len)
        {
            if (!this.isConnected || this.outputStream == null) {
                return -1;
            }
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {   //发送
                        outputStream.write(bytes, offset, len);
                        outputStream.flush();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            return 0;
        }

        public void Close() {
            this.isConnected = false;
            try {
                if (this.outputStream != null) {
                    this.outputStream.close();
                }
                if (this.inputStream != null) {
                    this.inputStream.close();
                }
                if (this.socket != null) {
                    this.socket.close();
                    sockets.remove(this);
                }
                Message msg = new Message();
                msg.what = ON_CLIENT_CLOSE;
                msg.obj = this.address;
                handler.sendMessage(msg);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
